package proxy

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sieve_engine/lib/config"
	"sieve_engine/lib/log"
	"strconv"
	"sync"
	"time"
)

var (
	proxyType   = *config.String("sieve.proxy.type", "proxy1", "proxy type")
	proxyConfig = underlayProxyConfig{
		ProxyConfig1: ProxyConfig1{
			ApiKey: *config.String("sieve.proxy1.api_key", "", "api_key"),
			Secret: *config.String("sieve.proxy1.secret", "", "secret"),
			proxyCommonConfig: proxyCommonConfig{
				Enable:       *config.Bool("sieve.proxy1.enable", true, "enable"),
				Debug:        *config.Bool("sieve.proxy1.debug", true, "debug"),
				Host:         *config.String("sieve.proxy1.host", "", "host"),
				CacheCount:   *config.Int("sieve.proxy1.cache_count", 1500, "cache_count"),
				ExprDuration: *config.Int("sieve.proxy1.expr_duration", 5, "expr_duration"),
			},
		},
		ProxyConfig2: ProxyConfig2{
			SigKey: *config.String("sieve.proxy2.sig_key", "eb819cdc41c2e9a0de63bc1f1daca27d", "sig_key"),
			Source: *config.String("sieve.proxy2.source", "37", "source"),
			proxyCommonConfig: proxyCommonConfig{
				Enable:       *config.Bool("sieve.proxy2.enable", true, "enable"),
				Debug:        *config.Bool("sieve.proxy2.debug", true, "debug"),
				Host:         *config.String("sieve.proxy2.host", "https://ground.dtdt888.com", "host"),
				CacheCount:   *config.Int("sieve.proxy2.cache_count", 1500, "cache_count"),
				ExprDuration: *config.Int("sieve.proxy2.expr_duration", 5, "expr_duration"),
			},
		},
	}
	notifyChan = make(chan struct{}, 32)
	proxyOnce  sync.Once
	proxyList  = NewMessageQueue()
)

type proxyNodes struct {
	Code int         `json:"Code"`
	Data []proxyNode `json:"Data"`
}

type proxyNode struct {
	Ip       string `json:"ip"`
	Port     string `json:"port"`
	Username string `json:"username"`
	Password string `json:"password"`
	Expr     int64  `json:"expr"`
}

type Socks5Detail struct {
	Username        string    `json:"username"`
	Password        string    `json:"password"`
	ClientId        int       `json:"clientId"`
	Country         string    `json:"country"`
	Timeout         int       `json:"timeout"`
	Ip              string    `json:"ip"`
	Port            int       `json:"port"`
	LastDeviceName  string    `json:"lastDeviceName"`
	LastChangeTime  time.Time `json:"lastChangeTime"`
	ChangeDeviceNow bool      `json:"changeDeviceNow"`
	CreatedAt       time.Time `json:"createdAt"`
	FlowDetail      int       `json:"flowDetail"`
	FlushFlow       bool      `json:"flushFlow"`
}

type Socks5Details struct {
	Data []Socks5Detail `json:"data"`
	Msg  string         `json:"msg"`
	Ret  int            `json:"ret"`
}

func (s Socks5Details) string() string {
	b, _ := json.Marshal(s)
	return string(b)
}

type underlayProxyConfig struct {
	ProxyConfig1 ProxyConfig1
	ProxyConfig2 ProxyConfig2
}

type proxyCommonConfig struct {
	Enable       bool
	Debug        bool
	Host         string `json:"host"`
	CacheCount   int    `json:"cache_count"`
	ExprDuration int    `json:"expr_duration"`
}

type ProxyConfig1 struct {
	ApiKey string `json:"api_key"`
	Secret string `json:"secret"`
	proxyCommonConfig
}

type ProxyConfig2 struct {
	SigKey string `json:"sig_key"`
	Source string `json:"source"`
	proxyCommonConfig
}

var (
	globalProxy = &proxyIpServiceProvider{}
)

func GetProxyCacheSize() int {
	return proxyList.Size()
}

func GetProxy(country string) string {

	var val string
	if proxyType == "proxy1" {
		p, _ := globalProxy.GetUnderlayProxy1(country)
		val = fmt.Sprintf("socks5://%s:%s@%s", p.Username, p.Password, fmt.Sprintf("%s:%v", p.Ip, p.Port))
	}

	if proxyType == "proxy2" {
		p, _ := globalProxy.GetUnderlayProxy2(country)
		val = fmt.Sprintf("socks5://%s:%s@%s", p.Username, p.Password, fmt.Sprintf("%s:%v", p.Ip, p.Port))
	}

	return val
}

type proxyIpServiceProvider struct {
}

func (provider *proxyIpServiceProvider) GetUnderlayProxy1(country string) (proxyNode, error) {

	var pConfig = proxyConfig.ProxyConfig1.proxyCommonConfig

	var (
		server       = pConfig.Host
		exprDuration = 300
		cacheCount   = 200
	)
	if pConfig.ExprDuration != 0 {
		exprDuration = pConfig.ExprDuration
	}
	if pConfig.CacheCount != 0 {
		cacheCount = pConfig.CacheCount
	}

	f := func() {
		for range notifyChan {

			if proxyList.Size() >= cacheCount {
				continue
			}

			var (
				requestCount = cacheCount - proxyList.Size()
				batchRCount  = 200
				qCount       = requestCount
				index        int
			)

			for {
				rCount := batchRCount
				if rCount > qCount {
					rCount = qCount
				}

				if qCount <= 0 {
					break
				}
				if pNode, err := provider.getUnderlayProxy1(server, country, rCount); err == nil {
					if pConfig.Enable {
						log.Info(fmt.Sprintf("请求接口获取的代理信息(%v 个 %v)", len(pNode), rCount))
					}
					var message []interface{}
					for _, n := range pNode {
						n.Expr = time.Now().Unix() + int64(exprDuration) + int64(index)
						index = index + 1
						message = append(message, n)
					}
					proxyList.PushMany(message)
				}
				qCount = qCount - rCount
			}
		}
	}
	proxyOnce.Do(func() {
		go f()
	})

	var (
		pn          proxyNode
		empty       = proxyNode{}
		fCheckValid = func(ele interface{}, size int) bool {

			/* 如果还在有效期内，或者缓存ip数量已经小于总数的一定比例的时候，将ip放回去继续使用一段时间 */
			if ele.(proxyNode).Expr > time.Now().Unix() || size < cacheCount/3 {
				return true
			}
			return false
		}
	)

	/* 每次都写入一次通知事件 */
	select {
	case notifyChan <- struct{}{}:
		break
	default:
		break
	}

	for pn == empty {

		ele, _, _ := proxyList.PopWithCallBack(fCheckValid)
		if ele == nil {
			time.Sleep(time.Millisecond * 5)
			continue
		}

		pn = ele.(proxyNode)
	}

	return pn, nil
}

func (provider *proxyIpServiceProvider) GetUnderlayProxy2(country string) (proxyNode, error) {

	var pConfig = proxyConfig.ProxyConfig2.proxyCommonConfig

	var (
		server       = pConfig.Host
		source       = proxyConfig.ProxyConfig2.Source
		exprDuration = 300
		cacheCount   = 200
	)

	if pConfig.ExprDuration != 0 {
		exprDuration = pConfig.ExprDuration
	}
	if pConfig.CacheCount != 0 {
		cacheCount = pConfig.CacheCount
	}

	f := func() {
		for range notifyChan {

			if proxyList.Size() >= cacheCount {
				continue
			}

			var (
				requestCount = cacheCount - proxyList.Size()
				batchRCount  = 3
				qCount       = requestCount
			)

			for {
				rCount := batchRCount
				if rCount > qCount {
					rCount = qCount
				}

				if qCount <= 0 {
					break
				}
				if pNode, err := provider.getUnderlayProxy2(server, source, country, rCount); err == nil {
					if pConfig.Enable {
						log.Info(fmt.Sprintf("请求接口获取的代理信息(%v 个 %v)", len(pNode), rCount))
					}
					for _, n := range pNode {
						n.Expr = time.Now().Unix() + int64(exprDuration)
						proxyList.Push(n)
					}
				}
				qCount = qCount - rCount
			}
		}
	}
	proxyOnce.Do(func() {
		if pNode, err := provider.getAllProxies2(server, source); err == nil {
			if pConfig.Enable {
				log.Info(fmt.Sprintf("初始加载代理信息(%v 个)", len(pNode)))
			}
			for _, n := range pNode {
				n.Expr = time.Now().Unix() + int64(exprDuration)
				proxyList.Push(n)
			}
		}
		go f()
	})

	var (
		pn    proxyNode
		empty = proxyNode{}
		got   bool
	)

	for pn == empty {
		for i := 0; i < cacheCount; i++ {
			ele, _ := proxyList.Seek()
			if ele == nil {
				continue
			} else {
				pn = ele.(proxyNode)
				got = true
				break
			}
		}

		select {
		case notifyChan <- struct{}{}:
			break
		default:
			break
		}
		if got {
			break
		} else {
			time.Sleep(time.Millisecond * 5)
		}
	}

	return pn, nil
}

func (provider *proxyIpServiceProvider) getAllProxies2(server, source string) ([]proxyNode, error) {
	/*
		1.客户端地址：https://client.dtdt888.com/
		2.请求地址：ground.dtdt888.com
		3.序列号 37
		4.key ：eb819cdc41c2e9a0de63bc1f1daca27d
		5.账号：llben 密码：password
	*/
	var (
		err        error
		requestURl = server
		sigKey     = proxyConfig.ProxyConfig2.SigKey
		t          = time.Now().Unix()
	)

	formData := url.Values{
		"source": {fmt.Sprintf("%v", source)},
		"ts":     {fmt.Sprintf("%v", t)},
	}

	v := fmt.Sprintf("/api/v1/socks5/list?%v&body=%v", formData.Encode(), sigKey)
	sig := fmt.Sprintf("%x", md5.Sum([]byte(v)))

	formData.Del("body")
	formData.Add("sig", sig)

	// 编码表单数据为 URL 编码形式
	formEncoded := formData.Encode()

	// 创建 GET 请求
	requestUrl := fmt.Sprintf("%v/api/v1/socks5/list?%v", requestURl, formEncoded)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		fmt.Println("Error creating request:", err)
		return nil, err
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Content-Length", fmt.Sprintf("%d", len(formEncoded)))

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error sending request:", err)
		return nil, err
	}
	defer resp.Body.Close()

	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// 处理响应
	if resp.StatusCode != http.StatusOK {
		return nil, err
	}

	var (
		sDetails = &Socks5Details{}
		pNodes   proxyNodes
	)
	if err := json.Unmarshal(b, sDetails); err != nil {
		return nil, err
	}

	// fmt.Println("获取的代理节点信息：", sDetails.string())

	for _, d := range sDetails.Data {
		pNodes.Data = append(pNodes.Data, proxyNode{
			Ip:       d.Ip,
			Port:     fmt.Sprintf("%v", d.Port),
			Username: d.Username,
			Password: d.Password,
		})
	}
	return pNodes.Data, nil
}

func (provider *proxyIpServiceProvider) getUnderlayProxy2(server, country, source string, cnt int) ([]proxyNode, error) {

	/*
		1.客户端地址：https://client.dtdt888.com/
		2.请求地址：ground.dtdt888.com
		3.序列号 37
		4.key ：eb819cdc41c2e9a0de63bc1f1daca27d
		5.账号：llben 密码：password
	*/
	var (
		err        error
		requestURl = server
		sigKey     = proxyConfig.ProxyConfig2.SigKey
		t          = time.Now().Unix()
	)

	formData := url.Values{
		"country": {country},
		"num":     {strconv.Itoa(cnt)},
		"source":  {fmt.Sprintf("%v", source)},
		"ts":      {fmt.Sprintf("%v", t)},
	}

	v := fmt.Sprintf("/api/v1/socks5/get?%v&body=%v", formData.Encode(), sigKey)
	sig := fmt.Sprintf("%x", md5.Sum([]byte(v)))

	formData.Del("body")
	formData.Add("sig", sig)

	// 编码表单数据为 URL 编码形式
	formEncoded := formData.Encode()

	// 创建 GET 请求
	requestUrl := fmt.Sprintf("%v/api/v1/socks5/get?%v", requestURl, formEncoded)
	req, err := http.NewRequest("GET", requestUrl, nil)
	if err != nil {
		fmt.Println("Error creating request:", err)
		return nil, err
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Content-Length", fmt.Sprintf("%d", len(formEncoded)))

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error sending request:", err)
		return nil, err
	}
	defer resp.Body.Close()

	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// 处理响应
	if resp.StatusCode != http.StatusOK {
		return nil, err
	}

	var (
		sDetails = &Socks5Details{}
		pNodes   proxyNodes
	)
	if err := json.Unmarshal(b, sDetails); err != nil {
		return nil, err
	}

	// fmt.Println("获取的代理节点信息：", sDetails.string())

	for _, d := range sDetails.Data {
		pNodes.Data = append(pNodes.Data, proxyNode{
			Ip:       d.Ip,
			Port:     fmt.Sprintf("%v", d.Port),
			Username: d.Username,
			Password: d.Password,
		})
	}
	return pNodes.Data, nil
}

func (provider *proxyIpServiceProvider) getUnderlayProxy1(server, country string, cnt int) ([]proxyNode, error) {

	var (
		err    error
		t      = time.Now().Unix()
		apiKey = proxyConfig.ProxyConfig1.ApiKey
		secret = proxyConfig.ProxyConfig1.Secret
	)
	formData := url.Values{
		"apiKey":  {apiKey},
		"t":       {fmt.Sprintf("%v", t)},
		"sig":     {fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("apiKey=%vt=%v%v", apiKey, t, secret))))},
		"country": {country},
		"cnt":     {fmt.Sprintf("%v", cnt)},
	}

	// 编码表单数据为 URL 编码形式
	formEncoded := formData.Encode()

	// 创建 POST 请求
	requestUrl := fmt.Sprintf("%v/QueryIPData", server)
	req, err := http.NewRequest("POST", requestUrl, bytes.NewBufferString(formEncoded))
	if err != nil {
		log.Err(fmt.Sprintf("获取代理(%v)失败:%+v", requestUrl, err))
		return nil, err
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Content-Length", fmt.Sprintf("%d", len(formEncoded)))

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Err(fmt.Sprintf("获取代理(%v)失败:%+v", requestUrl, err))
		return nil, err
	}
	defer resp.Body.Close()

	b, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// 处理响应
	if resp.StatusCode != http.StatusOK {
		return nil, err
	}

	var (
		pNodes proxyNodes
	)
	if err := json.Unmarshal(b, &pNodes); err != nil {
		return nil, err
	}

	return pNodes.Data, nil
}
